/*
 * UartContext.cpp
 *
 *  Created on: Sep 5, 2017
 *      Author: guoxs
 */
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <memory.h>
#include <termio.h>
#include <sys/ioctl.h>

#include "uart/UartContext.h"
#include "utils/Log.h"

#define UART_DATA_BUF_LEN 16384 // 16KB

static UartContext* uart0 = NULL;
static UartContext* uart1 = NULL;


extern int parseProtocol(const BYTE *pData, UINT len);
extern int User_parseProtocol(const BYTE *pData, UINT len);

static const char *getBaudRate(UINT baudRate)
{
   struct
   {
      UINT baud;
      const char *pBaudStr;
   } baudInfoTab[] = {
       {B1200, "B1200"},
       {B2400, "B2400"},
       {B4800, "B4800"},
       {B9600, "B9600"},
       {B19200, "B19200"},
       {B38400, "B38400"},
       {B57600, "B57600"},
       {B115200, "B115200"},
       {B230400, "B230400"},
       {B460800, "B460800"},
       {B921600, "B921600"}};

   int len = sizeof(baudInfoTab) / sizeof(baudInfoTab[0]);
   for (int i = 0; i < len; ++i)
   {
      if (baudInfoTab[i].baud == baudRate)
      {
         return baudInfoTab[i].pBaudStr;
      }
   }

   return NULL;
}

UartContext::UartContext() : mIsOpen(false),
                             mUartID(0),
                             mDataBufPtr(NULL),
                             mDataBufLen(0)
{
}

UartContext::~UartContext()
{
   delete[] mDataBufPtr;
   closeUart();
}

bool UartContext::openUart(const char *pFileName, UINT baudRate)
{
   LOGD("openUart pFileName = %s, baudRate = %s\n", pFileName, getBaudRate(baudRate));
   mUartID = open(pFileName, O_RDWR | O_NOCTTY);

   if (mUartID <= 0)
   {
      mIsOpen = false;
   }
   else
   {
      struct termios oldtio = {0};
      struct termios newtio = {0};
      tcgetattr(mUartID, &oldtio);

//      		newtio.c_cflag = baudRate|CS8|CLOCAL|CREAD; // 无校验 CLOCAL
      newtio.c_cflag = baudRate | CS8 | PARENB | CREAD; // 偶校验 PARENB

      newtio.c_iflag = 0;                               // IGNPAR | ICRNL
      newtio.c_oflag = 0;
      newtio.c_lflag = 0;     // ICANON
      newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
      newtio.c_cc[VMIN] = 1;  /* blocking read until 1 character arrives */
      tcflush(mUartID, TCIOFLUSH);
      tcsetattr(mUartID, TCSANOW, &newtio);

      // 设置为非阻塞 Set to non-blocking
      fcntl(mUartID, F_SETFL, O_NONBLOCK);

      mIsOpen = run("uart");
      if (!mIsOpen)
      {
         close(mUartID);
         mUartID = 0;
      }

      LOGD("openUart mIsOpen = %d\n", mIsOpen);
   }

   return mIsOpen;
}


bool UartContext::openUart(const char *pFileName, UINT baudRate,UINT check)
{
   LOGD("openUart pFileName = %s, baudRate = %s\n", pFileName, getBaudRate(baudRate));
   mUartID = open(pFileName, O_RDWR | O_NOCTTY);

   if (mUartID <= 0)
   {
      mIsOpen = false;
   }
   else
   {
      struct termios oldtio = {0};
      struct termios newtio = {0};
      tcgetattr(mUartID, &oldtio);

      //		newtio.c_cflag = baudRate|CS8|CLOCAL|CREAD; // 无校验 CLOCAL
      newtio.c_cflag = baudRate | CS8 | PARENB | CREAD; // 偶校验 PARENB

      newtio.c_iflag = 0;                               // IGNPAR | ICRNL
      newtio.c_oflag = 0;
      newtio.c_lflag = 0;     // ICANON
      newtio.c_cc[VTIME] = 0; /* inter-character timer unused */
      newtio.c_cc[VMIN] = 1;  /* blocking read until 1 character arrives */
      tcflush(mUartID, TCIOFLUSH);
      tcsetattr(mUartID, TCSANOW, &newtio);

      // 设置为非阻塞 Set to non-blocking
      fcntl(mUartID, F_SETFL, O_NONBLOCK);

      mIsOpen = run("uart");
      if (!mIsOpen)
      {
         close(mUartID);
         mUartID = 0;
      }

      LOGD("openUart mIsOpen = %d\n", mIsOpen);
   }

   return mIsOpen;
}
void UartContext::closeUart()
{
   LOGD("closeUart mIsOpen: %d...\n", mIsOpen);
   if (mIsOpen)
   {
      requestExit();

      close(mUartID);
      mUartID = 0;
      mIsOpen = false;
   }
}

bool UartContext::send(const BYTE *pData, UINT len)
{
   if (!mIsOpen)
   {
      return false;
   }

   if (write(mUartID, pData, len) != (int)len)
   { // fail
      LOGD("send Fail\n");
      return false;
   }

   // success
   //	LOGD("send Success\n");

   return true;
}

UartContext *UartContext::getInstance()
{
   static UartContext sUC;
   return &sUC;
}

bool UartContext::readyToRun()
{
   if (mDataBufPtr == NULL)
   {
      mDataBufPtr = new BYTE[UART_DATA_BUF_LEN];
   }

   if (mDataBufPtr == NULL)
   {
      closeUart();
   }

   return (mDataBufPtr != NULL);
}

bool UartContext::threadLoop()
{
   if (mIsOpen)
   {
      // 可能上一次解析后有残留数据，需要拼接起来
      // There may be residual data after the last analysis, which needs to be spliced
      int readNum = read(mUartID, mDataBufPtr + mDataBufLen, UART_DATA_BUF_LEN - mDataBufLen);

      if (readNum > 0)
      {
         mDataBufLen += readNum;

         // 解析协议
         // Parse protocol
         int len = User_parseProtocol(mDataBufPtr, mDataBufLen);
         if ((len > 0) && (len < mDataBufLen))
         {
            // 将未解析的数据移到头部
            // Move unparsed data to the head
            memcpy(mDataBufPtr, mDataBufPtr + len, mDataBufLen - len);
         }

         mDataBufLen -= len;
      }
      else
      {
         Thread::sleep(5);
      }

      return true;
   }

   return false;
}

// static UartContext* uart1 = NULL;
// static UartContext* uart2 = NULL;
// static UartContext* uart3 = NULL;
//
// void UartContext::init() {
//     uart1 = new UartContext(UART_TTYS0);
//	uart1->openUart("/dev/ttyS1", B9600);
////
////    uart2 = new UartContext(UART_TTYS1);
////	uart2->openUart("/dev/ttyS2", B38400);
////
////    uart3 = new UartContext(UART_TTYS3);
////	uart3->openUart("/dev/ttyS3", B38400);
//
//}

// void UartContext::destroy() {
//     if (uart1) {
//         delete uart1;
//         uart1 = NULL;
//     }
//     if (uart2) {
//         delete uart2;
//         uart2 = NULL;
//     }
//     if (uart3) {
//         delete uart3;
//         uart3 = NULL;
//     }
// }
//
// bool UartContext::sendTo(int uart, const BYTE* pData, UINT len) {
//     switch (uart) {
//     case UART_TTYS0:
//         return uart1->send(pData, len);
//     case UART_TTYS1:
//         return uart2->send(pData, len);
//     case UART_TTYS3:
//         return uart3->send(pData, len);
//     }
//     LOGD("无效的串口号");
//     return false;
// }
